// Copyright 2001-2016 Crytek GmbH / Crytek Group. All rights reserved.

#include "StdAfx.h"
#include "FlowConditionNode.h"

void CFlowNode_Condition::GetConfiguration(SFlowNodeConfig& config)
{
	static const SInputPortConfig in_config[] = {
		InputPortConfig<bool>("condition", _HELP("Setting this input to TRUE sends the value [value] to the [true] port, otherwise to [false]")),
		InputPortConfig_AnyType("value",   _HELP("Value to direct to a true or false port")),
		{ 0 }
	};
	static const SOutputPortConfig out_config[] = {
		OutputPortConfig_AnyType("true",  _HELP("Activated when the condition is true (passes the value through)")),
		OutputPortConfig_AnyType("false", _HELP("Activated when the condition is false (passes the value through)")),
		{ 0 }
	};
	config.sDescription = _HELP("LEGACY! Node to route data flow based on a boolean condition. Both inputs trigger the output.\nLegacy. Use Logic:Condition instead!");
	config.pInputPorts = in_config;
	config.pOutputPorts = out_config;
	config.SetCategory(EFLN_APPROVED);
}

void CFlowNode_Condition::ProcessEvent(EFlowEvent event, SActivationInfo* pActInfo)
{
	switch (event)
	{
	case eFE_Activate:
	case eFE_Initialize:
		bool bCondition = GetPortBool(pActInfo, 0);

		if (bCondition)
			ActivateOutput(pActInfo, 0, GetPortAny(pActInfo, 1));
		else
			ActivateOutput(pActInfo, 1, GetPortAny(pActInfo, 1));
	}
	;
};

class CFlowNode_LogicCondition : public CFlowBaseNode<eNCT_Instanced>
{
	enum
	{
		INP_Condition = 0,
		INP_In,
		INP_CondFalse,
		INP_CondTrue
	};

	enum
	{
		OUT_OnFalse = 0,
		OUT_OnTrue
	};

protected:
	bool m_condition;

public:
	CFlowNode_LogicCondition(SActivationInfo*) : m_condition(false) {}

	IFlowNodePtr Clone(SActivationInfo* pActInfo)
	{
		return new CFlowNode_LogicCondition(pActInfo);
	}

	void Serialize(SActivationInfo*, TSerialize ser)
	{
		ser.Value("condition", m_condition);
	}

	void GetConfiguration(SFlowNodeConfig& config)
	{
		static const SInputPortConfig in_config[] = {
			InputPortConfig<bool>("Condition",   _HELP("If condition is FALSE input [In] will be routed to [OnFalse], otherwise to [OnTrue]")),
			InputPortConfig_AnyType("In",        _HELP("Value to route to [OnFalse] or [OnTrue], based on [Condition]")),
			InputPortConfig_AnyType("CondFalse", _HELP("Sets [Condition] to false.")),
			InputPortConfig_AnyType("CondTrue",  _HELP("Sets [Condition] to true.")),
			{ 0 }
		};
		static const SOutputPortConfig out_config[] = {
			OutputPortConfig_AnyType("OnFalse", _HELP("Set to input [In] if [Condition] is FALSE)")),
			OutputPortConfig_AnyType("OnTrue",  _HELP("Set to input [In] if [Condition] is TRUE")),
			{ 0 }
		};
		config.sDescription = _HELP("Node to route data flow based on a boolean condition.\nSetting input [Value] will route it either to [OnFalse] or [OnTrue].");
		config.pInputPorts = in_config;
		config.pOutputPorts = out_config;
		config.SetCategory(EFLN_APPROVED);

	}

	virtual void GetMemoryUsage(ICrySizer* s) const
	{
		s->Add(*this);
	}

	void ProcessEvent(EFlowEvent event, SActivationInfo* pActInfo)
	{
		switch (event)
		{
		case eFE_Initialize:
			m_condition = GetPortBool(pActInfo, INP_Condition);
			break;

		case eFE_Activate:
			if (IsPortActive(pActInfo, INP_Condition))
				m_condition = GetPortBool(pActInfo, INP_Condition);
			if (IsPortActive(pActInfo, INP_CondFalse))
				m_condition = false;
			if (IsPortActive(pActInfo, INP_CondTrue))
				m_condition = true;

			// only port [In] triggers an output
			if (IsPortActive(pActInfo, INP_In))
			{
				if (m_condition == false)
					ActivateOutput(pActInfo, OUT_OnFalse, GetPortAny(pActInfo, INP_In));
				else
					ActivateOutput(pActInfo, OUT_OnTrue, GetPortAny(pActInfo, INP_In));
			}
			break;
		}
	}
};

class CFlowNode_LogicConditionInverse : public CFlowBaseNode<eNCT_Instanced>
{
	enum EInputs
	{
		IN_CONDITION = 0,
		IN_CONDFALSE,
		IN_CONDTRUE,
		IN_TRUE,
		IN_FALSE,
	};

protected:
	bool m_condition;

public:
	CFlowNode_LogicConditionInverse(SActivationInfo*) : m_condition(false) {}

	IFlowNodePtr Clone(SActivationInfo* pActInfo)
	{
		return new CFlowNode_LogicConditionInverse(pActInfo);
	}

	void Serialize(SActivationInfo*, TSerialize ser)
	{
		ser.Value("condition", m_condition);
	}

	void GetConfiguration(SFlowNodeConfig& config)
	{
		static const SInputPortConfig in_config[] = {
			InputPortConfig<bool>("Condition",   _HELP("If condition is FALSE input [False] will be routed to output, otherwise input [True] will be")),
			InputPortConfig_AnyType("CondFalse", _HELP("Sets [Condition] to false.")),
			InputPortConfig_AnyType("CondTrue",  _HELP("Sets [Condition] to true.")),
			InputPortConfig_AnyType("True",      _HELP("value to redirect to output when [Condition] is TRUE)")),
			InputPortConfig_AnyType("False",     _HELP("value to redirect to output when [Condition] is FALSE)")),
			{ 0 }
		};
		static const SOutputPortConfig out_config[] = {
			OutputPortConfig_AnyType("Out", _HELP("Value to output from [False] or [True], based on [Condition]")),
			{ 0 }
		};
		config.sDescription = _HELP("Node to route data flow based on a boolean condition.\nDepending on [Condition], either input [True] or [False] will be redirected to [out].");
		config.pInputPorts = in_config;
		config.pOutputPorts = out_config;
		config.SetCategory(EFLN_APPROVED);

	}

	virtual void GetMemoryUsage(ICrySizer* s) const
	{
		s->Add(*this);
	}

	void ProcessEvent(EFlowEvent event, SActivationInfo* pActInfo)
	{
		switch (event)
		{
		case eFE_Initialize:
			m_condition = GetPortBool(pActInfo, IN_CONDITION);
			break;

		case eFE_Activate:
			{
				if (IsPortActive(pActInfo, IN_CONDITION))
					m_condition = GetPortBool(pActInfo, IN_CONDITION);
				if (IsPortActive(pActInfo, IN_CONDFALSE))
					m_condition = false;
				if (IsPortActive(pActInfo, IN_CONDTRUE))
					m_condition = true;

				if (m_condition)
				{
					if (IsPortActive(pActInfo, IN_TRUE))
						ActivateOutput(pActInfo, 0, GetPortAny(pActInfo, IN_TRUE));
				}
				else
				{
					if (IsPortActive(pActInfo, IN_FALSE))
						ActivateOutput(pActInfo, 0, GetPortAny(pActInfo, IN_FALSE));
				}
				break;
			}
		}
	}
};

REGISTER_FLOW_NODE("Condition", CFlowNode_Condition)
REGISTER_FLOW_NODE("Logic:Condition", CFlowNode_LogicCondition)
REGISTER_FLOW_NODE("Logic:ConditionInverse", CFlowNode_LogicConditionInverse)
